﻿#define _CRT_SECURE_NO_WARNINGS 1;

#include<stdio.h>
#define NDEBUG
#include<assert.h>
//int main() {
//	int m = 10;
//	//m是可以被修改的
//	m = 20;
//	const int n = 20;
//	printf("%d\n", n);
//	//用const关键字修饰的变量n是不可以被修改的
//	//n = 10;
//	int* pn = &n;
//	//但是可以通过指针修改n变量的值
//	*pn = 10;
//
//	printf("%d\n", n);
//	return 0;
//}

//const修饰指针变量
//int main() {
//	int n = 10;
//
//	int m = 0;
//	//const关键字在'*'的左边，表示指针变量指向的地址中的值不可以被修改
//	const int* pn = &n;
//	//*pn = 20;
//	pn = &m;
//
//	int* const pm = &m;
//	*pm = 20;
//	//const关键字在'*'的右边，表示指针变量的值不可以被修改
//	//pm = &n;
//
//}

//野指针
//概念：野指针就是指针指向未知位置的指针
//野指针成因：1.指针变量未初始化
//int main() {
//	int* p;
//	*p = 20;
//
//	return 0;
//}
// 
// 
//野指针成因 2：指针越界访问
//int main() {
//	int arr[10] = { 0 };
//	int* p = &arr[0];
//	for (int i = 0; i < 12; i++) {
//		//当指针指向的范围超出数组
//		*(p + i) = i;
//	}
//	return 0;
//}
//
// 3.指针指向的空间释放
//int* test() {
//	int n = 100;
//	return &n;
//}
//
//int main() {
//	int* p = test();
//	printf("%d\n", *p);
//	return 0;
//}

//如何规避野指针
//1.定义指针变量时进行初始化
//int main() {
//	int num = 10;
//	int* p1 = &num;
//	int* p2 = NULL;
// 
//	return 0;
//}

//2.小心指针越界
//一个程序向内存申请了哪些空间，通过指针也只能访问哪些空间，不能超出访问范围，超出了就是越界访问
//当指针变量不再使用时及时赋值为null
//一个约定俗称的规则是：当指针不再使用之后就将其赋为null，使用一个指针变量之前都要检查这个指针变量是否为null
//int main() {
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int* p = &arr[0];
//	int i = 0;
//	for (int i = 0; i < 10; i++) {
//		*(p++) = i;
//	}
//
//	p = NULL;
//	p = &arr[0];
//	if (p != NULL) {
//		//...
//	}
//	return 0;
//}

//避免返回局部变量的地址，在函数调用结束之后，函数内定义的局部变量会返回给内存

//assert断言：assert.h 头⽂件定义了宏 assert() ，⽤于在运⾏时确保程序符合指定条件，如果不符合，就报
//错终⽌运⾏。这个宏常常被称为“断⾔”。

//利用指针模拟字符串库函数strlen统计字符串长度

//字符串的长度一定是一个大于等于0的值,返回值可以设置为size_t
//size_t mystrlen(char* pc) {
//	unsigned int count = 0;
//	char* tmp = pc;
//	assert(tmp != NULL);
//	while ((*tmp) != '\0') {
//		count++;
//		tmp++;
//	}
//	return count;
//}
//
//
////测试自己实现的strlen函数
//int main() {
//	char arr[] = "hello world";
//	int size = strlen(arr);
//	printf("%d\n", size);
//	size = mystrlen(arr);
//	printf("%d\n", size);
//	return 0;
//}

//传值调用和传址调用
//例子：写一个函数交换两个整型变量的值

//void swap1(int num1, int num2) {
//	int tmp = num1;
//	num1 = num2;
//	num2 = tmp;
//	return;
//}
//
//int main() {
//	int num1 = 10;
//	int num2 = 20;
//	printf("交换前两个变量的值：\n");
//	printf("num1:%d num2 %d\n", num1, num2);
//	swap1(num1, num2);
//	printf("交换后两个变量的值：\n");
//	printf("num1:%d num2 %d\n", num1, num2);
//	return 0;
//}

//通过以上示例代码的结果，可以发现两个变量的值并没有交换过来
//经过调试可以发现，在函数的参数列表中定义的形参的地址和主函数中传递给函数的实参的地址并不相等
//函数中确实完成了两个变量值的交换，但是由于形参和实参的内存地址并不相同。在swap1函数执行结束之后
//主函数中的需要交换值的变量并没有发生变化，这就是C语言的按值传递
//那如果我们坚持要交换两个变量的值怎么办呢？
//这个时候指针的作用就显现出来了

//void swap2(int *pn1, int *pn2) {
//	assert(pn1 != NULL);
//	assert(pn2 != NULL);
//	int tmp = *pn1;
//	*pn1 = *pn2;
//	*pn2 = tmp;
//	return;
//}
//
//int main() {
//	int num1 = 10;
//	int num2 = 20;
//	int* pn1 = &num1;
//	int* pn2 = &num2;
//	printf("交换前两个变量的值：\n");
//	printf("num1:%d num2 %d\n", num1, num2);
//	swap2(pn1, pn2);
//	printf("交换后两个变量的值：\n");
//	printf("num1:%d num2 %d\n", num1, num2);
//	pn1 = NULL;
//	pn2 = NULL;
//	return 0;
//}



